home *** CD-ROM | disk | FTP | other *** search
/ Fritz: All Fritz / All Fritz.zip / All Fritz / FILES / PROGSCAL / BTREE4.LZH / BTREE4.DOC next >
Text File  |  1988-01-17  |  26KB  |  661 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.                                   BTREE4
  29.  
  30.                 a Shareware Unit for Turbo Pascal ver. 4.0
  31.  
  32.                     for Data and Index file management
  33.  
  34.  
  35.                     Copyright (c) 1988 by W. Lee Passey
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.                              Pass-Key Software
  48.                             119 MacArthur Ave.
  49.                          Salt Lake City, UT  84115
  50.                           (801) 486-9239 (voice)
  51.                           (801) 485-7211 (data) 
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.      BTree4 is  a separately  compiled unit  for Turbo  Pascal ver. 4.0 from
  75. Borland International, Inc. BTree4  may be  linked to  a user's  source pro-
  76. grams,  and  will  performs  all  of  the  same B-tree indexing functions as
  77. Borland's Database Toolbox.
  78.  
  79.      This file contains general information about BTree4, an  annotated copy
  80. of the  interface segment  describing all variables and procedures available
  81. to calling procedures, and licensing and  registration information.   PLEASE
  82. READ THIS FILE COMPLETELY BEFORE ATTEMPTING TO USE BTREE4.
  83.  
  84. BTree4  has  been  designed  to  be as compatible as possible with Borland's
  85. Database Toolbox, but it  is not  a modification  of Borland's  source code;
  86. rather it  is a whole new set of procedures and functions using a compatible
  87. calling interface, but a wholly different memory storage  technique.  BTree4
  88. does not require any pre-defined constants or initializaton sequence, as all
  89. data storage, including the index page  stack, is  created as  needed on the
  90. heap.
  91.  
  92.      For maximum  flexibility, the BTree4 routines will not halt the program
  93. for any IO or heap full errors.  If an error is detected, the global boolean
  94. variable 'OK'  is set to false, and the global integer variable 'IOError' is
  95. set equal to the IO error code.   If the  error was  caused by  the unavail-
  96. ability of heap memory, 'IOError' will be set to -1.
  97.  
  98.      Disk  IO  errors  must  be  resolved  by the programmer, however memory
  99. allocation errors can usually  be  resolved  by  the  use  of  the procedure
  100. 'CheckMem'   in   conjunction   with   the   global  variable  'MaxPageMem.'
  101. 'MaxPageMem' should be set by the programmer to  the maximum  amount of heap
  102. memory which  will be allocated for the index page stack.  If the page stack
  103. attempts to exceed this amount of allocated memory,  little used  pages will
  104. be discarded from memory until the page stack is again under the limit.  The
  105. program expects that the amount of memory available will never  be less than
  106. the value stored in 'MinPageMem', and the stack size will never be decreased
  107. to less than that  value; thus,  'MaxPageMem' should  always be  larger than
  108. 'MinPageMem.'
  109.  
  110.      Both 'MaxPageMem'  and 'MinPageMem'  can be dynamically assigned values
  111. at any time during the operation of the program.  If more  dynamic memory is
  112. needed,  the  page  stack  can  be  reduced in size by reducing the value of
  113. 'MaxPageMem' (and 'MinPageMem', if necessary) and then calling CheckMem with
  114. a zero  parameter.  The only restriction is that 'MinPageMem' must always be
  115. large enough to hold one page from each  level of  the tree,  plus one extra
  116. page.  
  117.  
  118.      In  addition  to  the  page  stack,  each open data file and index file
  119. allocates for itself an IO buffer equal in size to the file's record length,
  120. and each  index file  allocates an extra page buffer which is the same size.
  121. These buffers are de-allocated  only by  closing the  associated file.   The
  122. record length  for index  files is  equal to  5 + (number of keys per page X
  123. (key length + 9)).
  124.  
  125.      What follows is a copy  of  the  BTree4  interface  section,  with each
  126. procedure and significant variable annotated as to its function and use.
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140. {$I-,V-}
  141.  
  142. unit btree4;                      { VERSION 1.0 }
  143.                                   { Copyright (c) 1988 by W. Lee Passey }
  144. interface
  145.  
  146. type
  147.   MaxString   = string[255];
  148.   bufarray    = array [1..80] of byte;
  149.   Str80       = string[80];
  150.  
  151.   PagePtr     = ^PageHeader;
  152.   KeyListPtr  = ^KeyList;
  153.  
  154.   DataFile    = record
  155.     case byte of
  156.       0 : (F      : file);
  157.       1 : (Handle,
  158.            Mode,
  159.            RecSize    : word;
  160.            Private    : array [1..26] of byte;        { reserved by TP4 }
  161.            FirstFree,         { the first available record in the file  }
  162.            NumberFree : longint;   { the number of records deleted and
  163.                                      therefore available for use        }
  164.            RecLength  : word;      { the length of records in this file }
  165.            KeysPerPage,            { if an index file, the maximum number
  166.                                      of keys which may be put on a page }
  167.            KeyLength  : byte;      { if an index file, the maximum
  168.                                      length of a key                    }
  169.            FirstPage  : longint;   { if an index file, the number of the
  170.                                      record which is the root page      }
  171.            FileName   : array [1..80] of char;
  172.            Buffer     : ^BufArray);{ pointer to a buffer on the heap,
  173.                                      used with this file, whose size is
  174.                                      identical to the record length     }
  175.            end;
  176.  
  177.   IndexFile   = record
  178.     DF        : DataFile;
  179.     RootPage  : PagePtr;           { a pointer to the root page, if it
  180.                                      is in memory                       }
  181.     KeySize,                       { the size of a key record, on the
  182.                                      heap, for keys in this file        }
  183.     ItemSize  : word;              { the size of a key, its reference
  184.                                      and record pointer, in this file   }
  185.     SavePage  : PagePtr;
  186.     SaveKey   : KeyListPtr;
  187.     SaveRec   : longint;
  188.     PageBuffer: ^BufArray;        { pointer to a buffer on the heap,
  189.                                     used with this file, whose size is
  190.                                     identical to the record length, and
  191.                                     which is used for packing and
  192.                                     unpacking pages to and from the disk
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205.                                     and memory.                         }
  206.     end;
  207.  
  208. (*   Like Borland's Database Toolbook, each file use by BTree4 must be
  209. declared either as a DataFile or an IndexFile.  *)
  210.  
  211.   PageHeader  = record
  212.     NextPage,                     { these two pointers link the pages   }
  213.     PrevPage,                     { into a two-way linked list in memory
  214.                                     each time a page is used it is placed
  215.                                     back at the head of this list       }
  216.     ParentPage,                   { a pointer to this page's parent page
  217.                                     which should be in memory           }
  218.     GreaterPage : PagePtr;        { a pointer to a page on a lower level
  219.                                     containing keys greater than all
  220.                                     keys on this page, if in memory     }
  221.     GreaterRec,                   { the disk record number of the page
  222.                                     which goes in GreaterPage           }
  223.     RecNum      : longint;        { the disk record number of this page }
  224.     FilePtr     : ^IndexFile;     { points to the file variable of the
  225.                                     file which this page comes from     }
  226.     ParentKey,                    { the key record from the parent page
  227.                                     which contains the key which is one
  228.                                     greater than all the keys on this
  229.                                     page.  if this pointer is nil, you
  230.                                     must go up another page; if this is
  231.                                     not possible you are in the root
  232.                                     page.                               }
  233.     KeyList,                      { pointer to the first key on page    }
  234.     ListEnd     : KeyListPtr;     { pointer to the last key on page     }
  235.     KeysOnPage  : byte;           { the number of keys actually in the
  236.                                     key list and on the page            }
  237.     end;
  238.  
  239.   KeyList     = record
  240.     NextKey,                      { these are the link pointers for the }
  241.     PrevKey     : KeyListPtr;     { list of keys                        }
  242.     LesserPage  : PagePtr;        { a pointer to a page on a lower level
  243.                                     containing keys less than all keys
  244.                                     on this page, if in memory          }
  245.     LesserRec,                    { the disk record number of the page
  246.                                     which goes into LesserPage          }
  247.     Reference   : longint;        { the data reference associated with
  248.                                     this key which usually is, but need
  249.                                     not be, the record number of a record
  250.                                     in a data file                      }
  251.     Key         : MaxString;      { this is the key.  Although declared
  252.                                     as a string of length 255, the actual
  253.                                     space available is only as much as the
  254.                                     keylength specified when making the
  255.                                     file }
  256.     end;
  257.  
  258. const
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.   MaxPageMem  : word = 16384;     { Maximum memory which will be allo-
  272.                                     cated for the page stack -- see above.
  273.                                     This variable is dynamic, and can be
  274.                                     adjusted by a calling program if need
  275.                                     be.                                 }
  276.   MinPageMem  : word = 2048;      { Minimum heap memory which MUST be
  277.                                     available for the page stack. This
  278.                                     amount should be at least equal to the
  279.                                     amount of memory required to hold one
  280.                                     page from each level of the tree plus
  281.                                     one page, each with its full comple-
  282.                                     ment of keys.                       }
  283.  
  284. var
  285.   OK          : boolean;          { status variable, true if all went
  286.                                     well, false otherwise.              }
  287.   IOError     : integer;          { if an IO error occured, this variable
  288.                                     will contain the error number, or -1
  289.                                     if the error is due to lack of
  290.                                     memory.                             }
  291.  
  292. procedure MakeFile (var DatF     : DataFile;
  293.                         FileName : Str80;
  294.                         RecLen   : word);
  295.  
  296. (*   This procedure creates a new data file named 'FileName' and having
  297. a record length of 'RecLen.'   Note that 'RecLen' is declared as type
  298. word, and thus the maximum record size allowed for a BTree4 data file
  299. is 65535 bytes.  Because of possible inaccuracies in counting the size
  300. of record variables it is recommended that programmers us the 'sizeof'
  301. function to pass the record length.  The specified record length is
  302. stored in the file header in record 0, and will always be used when the
  303. file is opened.  The minimum record length is 16 bytes and if 'RecLen'
  304. is less than this, a record length of 16 will be used.  If OK is false
  305. on return, an IO error occured.  *)
  306.  
  307. procedure OpenFile (var DatF     : DataFile;
  308.                         FileName : Str80);
  309.  
  310. (*   This procedure opens the fles specified by 'FileName' as a data file
  311. and assigns the file to the variable 'DatF.'  Note that unlike the Data-
  312. base Toolbox, no record length is passed, as the record length is fixed
  313. when making the file, and is extracted when the file is opened.  If OK
  314. is false on return, an IO error occured.  *)
  315.  
  316. procedure PutRec (var DatF   : DataFile;
  317.                       RecNum : longint;
  318.                   var Buffer            );
  319.  
  320. (*   This procedure takes 'RecLen' number of bytes from the variable
  321. 'Buffer' and writes it to the file as record number 'RecNum.'  If
  322. 'Buffer' is not large enough to fill the disk record, extra garbage
  323. will also be written.  If OK is false on return, an IO error occured.  *)
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337. procedure AddRec (var DatF   : DataFile;
  338.                   var RecNum : longint;
  339.                   var Buffer          );
  340.  
  341. (*   This procedure will add a record to the file 'DatF' containing the
  342. data from the variable 'Buffer.'  'RecNum' will return the number of the
  343. record added.  The new record may be at the end of the file, but need not
  344. be.  As records are deleted from the file they are not physically removed
  345. byt are simply put in a list of available records, and when a new record
  346. is needed the most recently deleted record is used.  The free list is
  347. maintained by a linked list which uses the first four bytes of each
  348. deleted file record.  It is recommended that these four bytes be reserved
  349. specifically for this use, so that a non-zero number in this position
  350. would indicate a free record, and a zero value would indicate a record in
  351. use.  This is useful both in "packing" a data file, and in re-creating
  352. an index file.  If OK is false upon return an IO error occured.  *)
  353.  
  354. procedure GetRec (var DatF   : DataFile;
  355.                       RecNum : longint;
  356.                   var Buffer            );
  357.  
  358. (*   This procedure reads the record number RecNum from the previously
  359. opened file DatF and places it into 'Buffer.'  It is the programmer's
  360. responsibility to insure that the variable passed as 'Buffer' is long
  361. enough to hold all the data, otherwise the procedure will possibly over-
  362. write other variables.   If OK is false upon return, an IO error occured.
  363. *)
  364.  
  365. procedure DeleteRec (var DatF   : DataFile;
  366.                          RecNum : longint);
  367.  
  368. (*   This procedure places the record specified by 'RecNum' on the list
  369. of available records, and fills the first four bytes of the record with
  370. $FFFFFFFF.  The next time a new record is added to the file, one of the
  371. from the available list will be used, and overwritten.  If OK is false
  372. upon return, an IO error has occurred.  *)
  373.  
  374. procedure CloseFile (var DatF : DataFile);
  375.  
  376. (*   This procedure updates the file header information and closes the
  377. file.  The file is closed even if an IO error occured while trying to
  378. save the header information.  The information remains, of course, in the
  379. file variable, until it is re-used, and may be extracted by an error
  380. handling routine.  If OK is false upon return, an IO error occured.  *)
  381.  
  382. function FileLen (var DatF : DataFile) : longint;
  383.  
  384. (*   Similar to the Database Toolbox, this function returns the number of
  385. records in DatF, both used and available.  This function is equivilent to
  386. "FileSize (DatF.F)" which can be used alternatively with less overhead.
  387. *)
  388.  
  389. function UsedRecs (var DatF : DataFile) : longint;
  390.  
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.  
  403. (*   Included primarily for compatability with the Database Toolbox, this
  404. function returns the number of used (non-available) records in the file.
  405. It is functionally equivilent to "FileSize (DatF.F) - DatF.NumberFree."
  406. *)
  407.  
  408. procedure MakeIndex (var IdxF         : IndexFile;
  409.                          FileName     : Str80;
  410.                          KeyLength,
  411.                          KeysPerPage  : byte);
  412.  
  413. (*   This procedure will create a new index file named 'FileName.'  It is
  414. similar to the procedure MakeFile, but instead of specifying the record
  415. length the programmer specifies the maximum length of a key ('KeyLength')
  416. and the maximum number of keys that can be placed on one page.  This
  417. information is used to calculate the record length which is
  418. 5 + (KeysPerPage * (KeyLength + 9)).  In addition to calculating the
  419. record length, this procedure initializes other data necessary to use
  420. as an index file.  Note that unlike the Database Toolbox it is not neces-
  421. sary to indicate if duplicate keys are allowed, as duplicate keys are
  422. always allowed in BTree4. If OK is false upon return, an IO error occured.
  423. *)
  424.  
  425. procedure OpenIndex (var IdxF     : IndexFile;
  426.                          FileName : Str80);
  427.  
  428. (*   This procedure opens the index specified by 'FileName' and initial-
  429. izes certain necessary data elements.  Note than information about the
  430. keys is not required as this information has been saved in the header
  431. in record 0.  *)
  432.  
  433. procedure CheckMem (Needed : word);
  434.  
  435. (*   This procedure checks the amount of memory on the heap used by the
  436. page stack, and if it is greater than that specified in the global vari-
  437. able 'MaxPageMem' plus that specified by 'Needed', little used pages, and
  438. their children, are removed from memory.  If the amount needed does not
  439. exceed 'MaxPageSize', but does exceed the maximum available memory, little
  440. used pages will be removed, until the amount needed is available, or until
  441. the stack size reaches 'MinPageMem'.  If the amount of memory needed
  442. cannot be made available, OK will be set to false, and IOError will be set
  443. to -1.   *)
  444.  
  445. procedure AddKey (var IdxF     : IndexFile;
  446.                   var RefAdded : longint;
  447.                       KeyAdded : MaxString);
  448.  
  449. (*   This procedure places the key passed as 'KeyAdded' into the index
  450. tree at the proper place in order.  If the length of the key passed is
  451. greater than that allowed for the index file, the key passed will be
  452. truncated.  The procedure gives no warning if a duplicate key is added,
  453. as duplicate keys are always allowed.  If the data reference is the
  454. same for both keys, the second one is not added, otherwise it is.  If
  455. you want to eliminate duplicate keys you must use the FindKey procedure
  456. before adding a key.  If OK is false upon return, an IO error has occured.
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468.  
  469. *)
  470.  
  471. procedure FindKey (var IdxF      : IndexFile;
  472.                    var RefFound  : longint;
  473.                        KeySought : MaxString);
  474.  
  475. (*   This procedure searches the index file for the FIRST occurance of
  476. the key sought.  If the length of the key passed is greater than that
  477. allowed for the index file, the key passed will be truncated.  If the
  478. key is found OK will be true upon return.  If OK is false and IOError
  479. is not zero then an IO error has occured.  *)
  480.  
  481. procedure SearchKey (var IdxF      : IndexFile;
  482.                      var RefFound  : longint;
  483.                      var KeySought : MaxString);
  484.  
  485. (*   Similar to FindKey, this procedure searches the index file for the
  486. first key which is equal to OR greater than 'KeySought.'  'KeySought'
  487. will return the value of the key found, and RefFound will return its
  488. reference.  If upon return OK is false and IOError is zero then there
  489. are no keys in the index file greater than or equal to the key sought. *)
  490.  
  491. procedure NextKey (var IdxF      : IndexFile;
  492.                    var RefFound  : longint;
  493.                    var KeySought : MaxString);
  494.  
  495. (*   This procedure will find the next key in the file after a call to
  496. any of the search procedures.  This procedure is used to perform a
  497. sequential search of index file.  To set the file to the beginning,
  498. use the procedure ClearKey.  After adding a record, a call to NextKey
  499. will find the key immediately after the key added.  'KeySought' will
  500. return the value of the key found, and RefFound will return its refer-
  501. ence.  If upon return OK is false but IOError is zero the end of the
  502. file has been reached.  Another call to NextKey at this point will
  503. return the first key in the index.  *)
  504.  
  505. procedure PrevKey (var IdxF      : IndexFile;
  506.                    var RefFound  : longint;
  507.                    var KeySought : MaxString);
  508.  
  509. (*   This procedure will find the key in the index file which is previous
  510. in order to the key last referenced.  This procedure is used to perform
  511. a reverse sequential search on the file.  To set the file to the end of
  512. the index use the the procedure ClearKey.  After adding a record, a call
  513. to PrevKey will find the key immediately prior to the key added.
  514. 'KeySought' will return the value of the key found, and RefFound will
  515. return its reference.  If upon return OK is false but IOError is zero,
  516. the beginning of the file has been reached.  Another call to PrevKey at
  517. this point will return the last key in the index.  *)
  518.  
  519. procedure ClearKey (var IdxF : IndexFile);
  520.  
  521. (*   This procedure sets the current index file position to the beginning
  522. (or end) of the index file.  After calling clear key a call to NextKey
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535. will return the first key in the index, and a call to PrevKey will return
  536. the last key in the index.  *)
  537.  
  538. procedure DeleteKey (var IdxF    : IndexFile;
  539.                          DataRef : longint;
  540.                          Key     : MaxString);
  541.  
  542. (*     This procedure removes a key from the index file, if it matches
  543. 'Key' AND 'DataRef'.  The double check is required because duplicate keys
  544. may exist in the index file, but not duplicate keys with the same data
  545. reference.  If the deletion of a key causes a page to have fewer than
  546. 'KeysPerPage' divided by two keys on the page, the page will be merged
  547. with a sibling.  If all keys are removed from the entire indexfile it
  548. will not be deleted, but will exist with only the header record.  *)
  549.  
  550. procedure CloseIndex (var IdxF : IndexFile);
  551.  
  552. (*   This procedure will update the header information and close the
  553. index file specified.  In addition, all pages currently in memory which
  554. were read from this file are purged and the memory is returned to the
  555. heap.  Like CloseFile, the file is closed even if the header information
  556. was not successfully saved, and an examination of the File variable will
  557. be necessary to save the information.  *)
  558.  
  559. procedure Unlink (var Link;
  560.                   var Head;
  561.                   var Tail);
  562.  
  563. (*   This procedure is a special bonus.  Unlink is used to maintain double
  564. linked lists of records on the heap, where the first four bytes of the
  565. record is a pointer to the next record, and the next four bytes is a
  566. pointer to the previous record.  The three parameters passed MUST be
  567. pointer types; 'Link' is a pointer to the record to be unlinked; 'Head'
  568. is the pointer to the head end of the linked list; and 'Tail' is the
  569. pointer to the tail end of the list.  Calling Unlink removes the record
  570. pointed to by 'Link' from the linked list, re-establishing all links,
  571. and updating the head pointer or tail pointer if necessary.  None of
  572. the pointers in 'Link^' are affected.  *)
  573.  
  574.  
  575.      BTree4 is  shareware, which  means that it, like most shareware, may be
  576. freely copied and distributed so long  as no  consideration is  required for
  577. its distribution,  except a  copying and  media charge  not to exceed $3.00,
  578. including the cost of the means of distribution (i.e., diskette).  Users who
  579. find the  program of  value to  them should  consider sending  a donation to
  580. Pass-Key Software.
  581.  
  582.      Users sending a donation of $25.00 or more are registered, will receive
  583. notification of upgrades and modifications to this product, and are entitled
  584. to receive source code and future updates, upon request, for  the cost  of a
  585. diskette and  postage.   Non-registered useres may not incorporate this unit
  586. into  any  commercially  distributed  software,  including  shareware, while
  587. registered users may do so freely.
  588.  
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.      BTree4 is  a copyrighted  program, and  is protected by the laws of the
  602. United States and each of its several states.  A  licence is  hereby granted
  603. to all  persons to  use this program according to the terms and restrictions
  604. contained herein.  All programs which  incorporate all  or any  part of this
  605. program must include the following phrase both in the source code and in any
  606. accompanying documentation:
  607.  
  608.      Portions of this program Copyright (c) 1988 by W. Lee Passey.
  609.  
  610.      This program is distributed as is, and by its use each person agrees to
  611. the  terms  and  conditions  of  this  license, and acknowledges that W. Lee
  612. Passey and Pass-Key Software have  made  no  warranties,  either  express or
  613. implied,  concerning  the  performance  of  this software, including that of
  614. fitness for a particular purpose.
  615.  
  616.      Please send all comments,  suggestions, information  regarding possible
  617. bugs, donations and registration information to:
  618.  
  619.                              Pass-Key Software
  620.                             119 MacArthur Ave.
  621.                          Salt Lake City, UT  84115
  622.  
  623. or use  your modem  to call The Motherboard, (801) 485-7211, 8 data, 1 stop,
  624. no parity, 300/1200/2400 baud, 24 hours/day, 7 days/week.
  625.  
  626.      I am also looking for a job in the data processing field, and this unit
  627. is a good example of my programming skills.  If any employers are interested
  628. in using me as an employee, please contact me in the same way.
  629.  
  630.  
  631.  
  632.  
  633.  
  634.  
  635.  
  636.  
  637.  
  638.  
  639.  
  640.  
  641.  
  642.  
  643.  
  644.  
  645.  
  646.  
  647.  
  648.  
  649.  
  650.  
  651.  
  652.  
  653.  
  654.  
  655.  
  656.  
  657.  
  658.  
  659.  
  660.  
  661.